package app.controllers;

import app.Const;
import app.constant.CommonStatusMap;
import app.constant.DictConstant;
import app.constant.MemberConstant;
import app.constant.ProductConstant;
import app.kit.CommonKit;
import app.kit.TypeKit;
import app.models.basic.ClearingRule;
import app.models.basic.Dict;
import app.models.product.Brand;
import app.models.product.ClearingTime;
import app.models.product.Details;
import app.models.product.Group;
import app.models.product.Issuer;
import app.models.product.Product;
import app.models.product.ProductYields;
import app.models.product.PurchaseLimit;
import app.models.sys.User;
import app.services.ProductService;
import app.services.SerialService;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jfinal.aop.Before;
import com.jfinal.ext.interceptor.GET;
import com.jfinal.ext.interceptor.POST;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import goja.GojaConfig;
import goja.StringPool;
import goja.date.DateFormatter;
import goja.mvc.Controller;
import goja.mvc.render.JxlsRender;
import goja.plugins.sqlinxml.SqlKit;
import goja.rapid.datatables.DTCriterias;
import goja.rapid.db.Condition;
import goja.rapid.db.DaoKit;
import goja.security.shiro.AppUser;
import goja.security.shiro.Securitys;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import static app.Const.ACTION_ATTR;
import static app.Const.CREATE_ACTION;
import static app.Const.DATE_SPLIT;
import static app.Const.EDIT_ACTION;
import static app.Const.FIELD_CODE;
import static app.Const.FIELD_COLLECTION_MODE;
import static app.Const.FIELD_CREATE_TIME;
import static app.Const.FIELD_NAME;
import static app.Const.FIELD_PRICE;
import static app.Const.FIELD_PRODUCT;
import static app.Const.FIELD_RISK_TOLERANCE;
import static app.Const.FIELD_STATUS;
import static app.Const.FIELD_TIME_LIMIT_UNIT;
import static app.Const.FIELD_YIELD;
import static app.Const.NO;
import static app.Const.Report;
import static app.Const.YES;
import static app.constant.ProductConstant.DEL;
import static app.constant.ProductConstant.END;
import static app.constant.ProductConstant.FAIL;
import static app.constant.ProductConstant.FOUND;
import static app.constant.ProductConstant.READY;

/**
 * 理财产品
 * liuhui on 15/2/11.
 */
public class ProductController extends Controller {


    @Before(GET.class)
    public void index() {
        //产品分组
        setAttr("groups", Group.dao.findAvalibleAll());
    }

    /**
     * 列表访问
     */
    @Before(GET.class)
    public void dtlist() {
        final DTCriterias criterias = DTCriterias.criteriasWithRequest(getRequest());
        criterias.setParam(FIELD_STATUS, Condition.NE, DEL);
        searchCondition(criterias);
        renderDataTables(criterias, Product.class);
    }

    /**
     * 设置查询条件
     *
     * @param criterias 查询条件
     */
    private void searchCondition(DTCriterias criterias) {
        //年利化率
        String p_yield_start = getPara("p_yield_start");
        String p_yield_end = getPara("p_yield_end");
        //起息日
        String p_due_date_start = getPara("p_due_date_start");
        String p_due_date_end = getPara("p_due_date_end");
        //项目本金
        String p_price_start = getPara("p_price_start");
        String p_price_end = getPara("p_price_end");
        //起投金额
        String p_min_invest_amount_start = getPara("p_min_invest_amount_start");
        String p_min_invest_amount_end = getPara("p_min_invest_amount_end");


        if (!Strings.isNullOrEmpty(p_yield_start)) {
            criterias.setParam(FIELD_YIELD, Condition.GTEQ, p_yield_start);
        }
        if (!Strings.isNullOrEmpty(p_yield_end)) {
            criterias.setParam(FIELD_YIELD, Condition.LTEQ, p_yield_end);
        }

        if (!Strings.isNullOrEmpty(p_due_date_start)) {
            criterias.setParam("valuedate_offset", Condition.GTEQ, p_due_date_start);
        }
        if (!Strings.isNullOrEmpty(p_due_date_end)) {
            criterias.setParam("valuedate_offset", Condition.LTEQ, p_due_date_end);
        }

        if (!Strings.isNullOrEmpty(p_price_start)) {
            criterias.setParam(FIELD_PRICE, Condition.GTEQ, p_price_start);
        }
        if (!Strings.isNullOrEmpty(p_price_end)) {
            criterias.setParam(FIELD_PRICE, Condition.LTEQ, p_price_end);
        }

        if (!Strings.isNullOrEmpty(p_min_invest_amount_start)) {
            criterias.setParam("min_invest_amount", Condition.GTEQ, p_min_invest_amount_start);
        }
        if (!Strings.isNullOrEmpty(p_min_invest_amount_end)) {
            criterias.setParam("min_invest_amount", Condition.LTEQ, p_min_invest_amount_end);
        }
    }

    @Before(GET.class)
    public void create() {
        Product product = new Product();
        product.set("valuedate_offset", 1);
        product.set("basic_days", 365);
        product.set("sale_time", 10);
        product.set("time_limit_unit", DictConstant.DAY_UNIT);
        product.set("due_date", DateTime.now().toDate());

        //发行机构
        setFormAttr();
        setAttr(ACTION_ATTR, CREATE_ACTION);
        setAttr(FIELD_PRODUCT, product);
        render("item.ftl");
    }

    @Before(GET.class)
    public void edit() {
        String id = getPara(0, "0");
        boolean readonly = !Strings.isNullOrEmpty(getPara(1));

        Product product = Product.dao.findById(id);
        setFormAttr();

        setAttr(ACTION_ATTR, EDIT_ACTION);
        setAttr(FIELD_PRODUCT, product);
        setAttr("readonly",readonly);
        render("item.ftl");
    }

    public void detail_view(){
        String id = getPara(0, "0");
        Product product = Product.dao.findById(id);
        setAttr(FIELD_PRODUCT, product);
        render("detail_view.ftl");
    }

    private void setFormAttr() {
        //发行机构
        setAttr("issuer", Issuer.dao.findAll());
        //所属品牌
        setAttr("brand", Brand.dao.findAll());
        setAttr("groups", Group.dao.findAll());
    }

    @Before(POST.class)
    public void save() {
        AppUser<User> loginUser = Securitys.getLogin();
        final List<String> errors = Lists.newArrayList();

        final Product product = getModel(Product.class);
        if (TypeKit.getInt(product, FIELD_COLLECTION_MODE) < 1) {
            errors.add("收益方式不能为空");
        }
        //处理表单中checkbox
        improveForProduct(product, errors);

        if (!errors.isEmpty()) {
            setAttr("errors", errors);
            setAttr(FIELD_PRODUCT, product);
            setAttr(ACTION_ATTR, getPara(Const.ACTION_ATTR));
            product.set("due_date", DateTime.now().toDate());
            setFormAttr();
            render("item.ftl");
            return;
        }

        final DateTime now = DateTime.now();
        if (DaoKit.isNew(product)) {
            product.set(FIELD_CODE, SerialService.productCode());
            product.set(FIELD_STATUS, READY);
            product.set(FIELD_CREATE_TIME, now);
            product.set("last_update_time", now);
            product.set("publish_time", now);
            final ProductYields productYields = new ProductYields();
            productYields.set(FIELD_YIELD, product.getNumber(FIELD_YIELD));
            productYields.set("init_flag", YES);
            product.set("remaining_amount", product.getBigDecimal(FIELD_PRICE));
            product.set("creater",loginUser.getId());
            product.set("creater_name",loginUser.getName());
            final PurchaseLimit purchaseLimit = new PurchaseLimit();
            Db.tx(new IAtom() {
                @Override
                public boolean run() throws SQLException {
                    if (product.save()) {
                        final int product_id = product.getNumber(StringPool.PK_COLUMN).intValue();
                        productYields.set(FIELD_PRODUCT, product_id);
                        purchaseLimit.set(FIELD_PRODUCT, product_id);
                        boolean save = purchaseLimit.save();
                        boolean save1 = productYields.save();
                        //产品结算时间
                        ClearingTime clearing = getClearingTime(product_id, product.getNumber(FIELD_COLLECTION_MODE).intValue());
                        boolean clearSave = clearing.save();
                        return save && save1 &&clearSave;
                    }
                    return false;
                }
            });
        } else {
            product.set("last_update_time", now.toDate());
            if (product.get("group_id") == null) {
                product.set("group_id", 0);
            }
            product.update();
            final int product_id = product.getNumber(StringPool.PK_COLUMN).intValue();

            ProductYields.dao.updateByInitProduct(product_id, product.getNumber(FIELD_YIELD).doubleValue());
        }
        redirect("/product");
    }

    private ClearingTime getClearingTime(int product_id, int collection_mode) {
        ClearingRule rule = ClearingRule.dao.getByProductType(collection_mode);
        ClearingTime clearing = new ClearingTime();
        clearing.set(FIELD_PRODUCT, product_id);

        Number clearing_time = rule.getNumber("clearing_time");
        if(null != clearing_time){
            clearing.set("clearing_time", clearing_time.intValue());
        }
        Number clearing_cycle = rule.getNumber("clearing_cycle");
        if(null != clearing_cycle){
            clearing.set("clearing_cycle", clearing_cycle.intValue());
        }
        Number clearing_day = rule.getNumber("clearing_day");
        if(null != clearing_day){
            clearing.set("clearing_day", clearing_day.intValue());
        }
        return clearing;
    }

    /**
     * 完善产品数据，包括业务规则
     *
     * @param product 产品
     * @param errors  错误提示信息
     */
    private void improveForProduct(final Product product, List<String> errors) {
        if(product.getNumber("transfer_holding_days") == null){
            product.set("transfer_holding_days", 0);
        }
        String valuedate_str = getPara("valuedate");

        //处理募集时间区间
        final String start_and_end_time = getPara("start_end");
        DateTime start_date;
        DateTime end_date;
        if (!Strings.isNullOrEmpty(start_and_end_time)) {
            final String[] times = start_and_end_time.split(DATE_SPLIT);

            start_date = CommonKit.parseSlashDate(times[0]);
            product.set("begin_date", start_date.toDate());
            end_date = CommonKit.parseSlashDate(times[1]);
            product.set("end_date", end_date.toDate());
        } else {
            start_date = DateTime.now();
            end_date = DateTime.now();
        }
        final DateTime valuedate;
        if (!Strings.isNullOrEmpty(valuedate_str)) {
            valuedate = DateTime.parse(valuedate_str, DateFormatter.DATE_TIME_PATTERN_YYYY_MM_DD);
            product.set("valuedate", valuedate);

        } else {
            errors.add("产品起息日期不能为空");
            valuedate= DateTime.now();
        }

        if(valuedate.isBefore(end_date)){
            errors.add("起息日期必须在销售结束日期之后");
        }

        //手机专享
        product.set("cell_phone", !Strings.isNullOrEmpty(getPara("product.cell_phone")) ? YES : NO);
        //新手专享
        product.set("new_user", !Strings.isNullOrEmpty(getPara("product.new_user")) ? YES : NO);
        //转让标记
        product.set("transfer_flag", !Strings.isNullOrEmpty(getPara("product.transfer_flag")) ? YES : NO);
        //赎回标记
        product.set("redeem_flag", !Strings.isNullOrEmpty(getPara("product.redeem_flag")) ? YES : NO);

        //产品到期时间 = 产品起息时间 ＋ 产品理财期限时间
        int time_limit = TypeKit.getInt(product, "time_limit");
        if (time_limit < 1) {
            errors.add("请输入理财期限");
            return;
        }
        String time_limit_unit = product.getStr("time_limit_unit");
        if (!Strings.isNullOrEmpty(time_limit_unit)) {
            if (StringUtils.equals(time_limit_unit, DictConstant.DAY_UNIT)) {
                // 起息日 包含在内算时间
                product.set("due_date", valuedate.plusDays(time_limit - 1));
            } else if (StringUtils.equals(time_limit_unit, DictConstant.MONTH_UNIT)) {
                product.set("due_date", valuedate.plusMonths(time_limit));
            } else {
                // 未知的理财q期限单位
                errors.add("理财期限单位只能为月或者天");
            }
        } else {
            errors.add("请选择理财期限单位");
            return;
        }
        //上传图片
        if (!Strings.isNullOrEmpty(getPara("pic_attachment"))) {
            product.set("logo", getPara("pic_attachment_url"));
            product.set("logo_attachment", getPara("pic_attachment"));
        }
        if (product.getNumber("issuer") == null) {
            product.set("issuer", 0);
        }
        if (product.getBigDecimal("group_id") == null) {
            product.set("group_id", 0);
        }

        //成立日期
        if(!Strings.isNullOrEmpty(getPara("founded_date"))){
            DateTime founded_date = DateTime.parse(getPara("founded_date"), DateFormatter.DATE_TIME_PATTERN_YYYY_MM_DD);
            product.set("founded_date", founded_date);
        }
        //到期日期
        if(!Strings.isNullOrEmpty(getPara("expire_date"))){
            DateTime expire_date = DateTime.parse(getPara("expire_date"), DateFormatter.DATE_TIME_PATTERN_YYYY_MM_DD);
            product.set("expire_date", expire_date);
        }

    }


    /**
     * 删除产品
     */
    public void delete() {
        int id = getParaToInt(0, 0);
        Product product = Product.dao.findById(id);
        boolean update = product.set(Const.FIELD_STATUS, DEL).update();
        if (update) {
            renderAjaxSuccess("删除成功");
        } else {
            renderAjaxFailure();
        }
    }


    /**
     * 设置启用禁用状态
     */
    public void setStatus() {
        String id = getPara("id", "0");
        String set_val = getPara("set_val");

        //上线直接进行修改
        if(set_val.equals(String.valueOf(ProductConstant.ONLINE))){
            boolean updateStatus = Product.dao.updateStatus(set_val, id);
            if (updateStatus) {
                renderAjaxSuccess();
            } else {
                renderAjaxFailure();
            }
            return;
        }

        Product pro = Product.dao.findById(id);
        int status = pro.getNumber(Const.FIELD_STATUS).intValue();
        //除了销售中的状态其他一律不给下架
        if(status!=ProductConstant.ONLINE){
            renderAjaxFailure("产品不能进行下架");
            return;
        }

        boolean buyStatus = Product.dao.checkBuy(id);
        if(buyStatus){
            renderAjaxFailure("产品已经被购买，不能下架");
            return;
        }

        boolean updateStatus = Product.dao.updateStatus(set_val, id);
        if (updateStatus) {
            renderAjaxSuccess();
        } else {
            renderAjaxFailure();
        }
    }

    /**
     * 设置首页推荐
     */
    public void recommend() {
        int productId = getParaToInt("id", 0);
        if (productId > 0) {
            String set_val = getPara("set_val");
            final Product product = Product.dao.findById(productId);

            if (null != product) {
                int status = TypeKit.getStatus(product);
                if (status == ProductConstant.FAIL ) {
                    renderAjaxFailure("产品募集失败，无法推荐到首页");
                    return;
                }else if (status == ProductConstant.READY ) {
                    renderAjaxFailure("产品待发布，无法推荐到首页");
                    return;
                }

                product.set("recommend", set_val);
                product.set("recommend_time", DateTime.now());
                boolean update = product.update();
                if (update) {
                    renderAjaxSuccess();
                } else {
                    renderAjaxFailure("首页推荐设置失败!");
                }
            } else {
                renderAjaxFailure("首页推荐设置失败!");
            }
        } else {
            renderAjaxFailure("首页推荐设置失败!");
        }
    }

    /**
     * 数据导出
     */
    public void export() {
        Map<String, Map<String, Dict>> dictMaps = Dict.dao.getDictMaps();
        List<Product> products = Product.dao.findExcelExport();
        for (Product product : products) {
            //收益方式
            String collection_mode_val = product.getNumber(FIELD_COLLECTION_MODE).toString();
            product.put("collection_mode_name", dictMaps.get(FIELD_COLLECTION_MODE).get(collection_mode_val).getStr(FIELD_NAME));
            //风险等级
            String risk_tolerance_val = product.getNumber(FIELD_RISK_TOLERANCE).toString();
            product.put("risk_tolerance_name", dictMaps.get("risk_level").get(risk_tolerance_val).getStr(FIELD_NAME));
            //期限单位
            String time_limit_unit_val = product.getStr(FIELD_TIME_LIMIT_UNIT);
            product.put("time_limit_unit_name", dictMaps.get("limit_unit").get(time_limit_unit_val).getStr(FIELD_NAME));
            //产品状态
            product.put("status_name", CommonStatusMap.getChineseProductStatus(product.getNumber(FIELD_STATUS).intValue()));
            //手机专享	新手专享	赎回标记	转让标记
            product.put("cell_phone_name", product.getStr("cell_phone").equals(YES) ? "是" : "否");
            product.put("new_user_name", product.getStr("new_user").equals(YES) ? "是" : "否");
            product.put("redeem_flag_name", product.getStr("redeem_flag").equals(YES) ? "是" : "否");
            product.put("transfer_flag_name", product.getStr("transfer_flag").equals(YES) ? "是" : "否");

        }
        Map<String, Object> _excel_datas = Maps.newHashMap();
        _excel_datas.put(FIELD_PRODUCT, products);
        JxlsRender beans = JxlsRender.me(Report.PRODUCT_PATH).filename("产品表.xls").beans(_excel_datas);
        render(beans);
    }

    /**
     * 产品结算时间
     */
    public void clearing(){
        int product_id = getParaToInt(0, 0);
        Product product = Product.dao.findById(product_id);
        if (null != product) {
            setAttr("product_name", product.getStr(FIELD_NAME));
        }
        ClearingTime clearing = ClearingTime.dao.findById(product_id);
        setAttr("c", clearing);
        render("clearing.ftl");
    }

    /**
     * 保存产品结算时间
     */
    public void clearing_save(){
        ClearingTime c = getModel(ClearingTime.class, "c");
        c.update();
        redirect("/product");
    }

    /**
     * 风险提示标记
     */
    public void risk_item(){
        int product_id = getParaToInt(0, 0);
        if(product_id > 0){
            Details details = Details.dao.findRiskByProduct(product_id);
            setAttr("product_id",product_id);
            setAttr("details", details);
            render("risk_item.ftl");
        }
    }

    /**
     * 风险提示书标记
     */
    public void risk_save(){
        Details details = getModel(Details.class);
        details.set("risk_flag", Strings.isNullOrEmpty(details.getStr("attribute_val").trim()) ? Const.NO : Const.YES);

        if(DaoKit.isNew(details)){
            details.save();
        }else {
            details.update();
        }
        redirect("/product");
    }

    /**
     *编辑状态检查
     */
    public void editeCheck(){
        String id = getPara("id");
        Product pro = Product.dao.findById(id);
        int status = pro.getNumber(Const.FIELD_STATUS).intValue();
        if(status==ProductConstant.READY ){
            renderAjaxSuccess();
            return;
        }

        if(status==ProductConstant.EMPTY || status == ProductConstant.END ||  status == ProductConstant.FAIL) {
            renderAjaxFailure("产品不能进行编辑，只能查看");
            return;
        }

        boolean buyStatus = Product.dao.checkBuy(id);
        if(buyStatus){
            renderAjaxFailure("产品已经上架，并且已被购买，不能进行编辑");
        }else{
            renderAjaxFailure("产品已经上架，不能进行编辑，请先下架后再进行编辑");
        }
    }

    /**
     * 删除检查
     * 1 未发布的
     * 2 上架的未购买的（可会对其他逻辑影响）
     * 其他都不给删除（避免统计查询出问题）
     */
    public void deleteCheck(){
        String id = getPara("id","0");
        Product pro = Product.dao.findById(id);
        int status = pro.getNumber(Const.FIELD_STATUS).intValue();

        //除了未发布和上架的zh直接不给删除
      if(status!=ProductConstant.READY && status!=ProductConstant.ONLINE){
            renderAjaxFailure("产品不能进行删除");
            return;
      }
        boolean buyStatus = Product.dao.checkBuy(id);
        if(buyStatus){
            renderAjaxFailure("产品已经被购买，不能删除");
            return;
        }
        renderAjaxSuccess();
    }


    /**
     * 设置产品募集失败
     */
    public void raisedfailure() {
        final int productId = getParaToInt(0, 0);
        if (productId > 0) {
            final Product product = Product.dao.findById(productId);
            if (product == null) {
                renderAjaxFailure("您选择的产品数据有问题，无法完成操作");
            } else {
                int status = TypeKit.getStatus(product);
                switch (status) {
                    case DEL:
                        renderAjaxFailure("产品已删除，无法完成募集失败操作");
                        break;
                    case FOUND:
                        renderAjaxFailure("产品已成立，无法完成募集失败操作");
                        break;
                    case END:
                        renderAjaxFailure("产品已结束，无法完成募集失败操作");
                        break;
                    case FAIL:
                        renderAjaxFailure("产品已募集失败");
                        break;

                    default:
                        // 1. 设置产品募集失败
                        product.set(Const.FIELD_STATUS, FAIL);

                        // 处理会员数据, 只需要募集失败订单
                        final int memberProductStatus = MemberConstant.PRODUCT_SALE_FAIL;

                        boolean ok = Db.tx(new IAtom() {
                            @Override
                            public boolean run() throws SQLException {
                                int records = Db.update(SqlKit.sql("memberproduct.updateProductStatus"), memberProductStatus, productId);
                                return product.update() && records >= 0;
                            }
                        });
                        if (ok) {
                            // 生成募集失败打款纪录
                            ProductService.me.generateFailMoney(productId);
                        }
                        renderAjaxSuccess();
                        break;

                }
            }
        } else {
            renderAjaxFailure();
        }
    }

    public void lifycle(){
        if(GojaConfig.isDev()){
            DateTime date = getDate("date");
            ProductService.me.statusCheck(date);
            renderAjaxSuccess();
        } else {
            renderError(404);
        }
    }

}
